import os
import datetime
import pandas
import glob
import ujson
import re
import functools
import pandas as pd

from coin.base.datetime_util import to_timestamp_int
from coin.exchange.kr_rest.product.product_impl import generate_product_from_str2
from coin.strategy.hk_hard_arb.util import clean_json
import coin.strategy.mm.tool.archive_base as abase
from coin2.service.strategy import trading_summary_service_pb2 as tss_pb2
from xunkemgmt_client.client.api_client import XunkemgmtClient
from collections.abc import Iterable


COIN2_RELATIVE_SYMBOL_MAP = {
    "CURRENT_QUARTER": "QUARTER",
    "CURRENT_WEEK": "WEEK",
}


@functools.lru_cache(None)
def read_sql_as_df(sql_tuple):
  return pandas.read_sql(*sql_tuple)


def get_strategy_info(trading_date, **query_dict):
  if isinstance(trading_date, Iterable) and not isinstance(trading_date, str):
    start_ts = to_timestamp_int(trading_date[0])
    end_ts = to_timestamp_int(trading_date[-1] + datetime.timedelta(days=1))
  elif isinstance(trading_date, datetime.datetime):
    start_ts = to_timestamp_int(trading_date)
    end_ts = to_timestamp_int(trading_date + datetime.timedelta(days=1))
  else:
    dt = datetime.datetime.strptime(trading_date, "%Y%m%d")
    start_ts = to_timestamp_int(dt)
    end_ts = to_timestamp_int(dt + datetime.timedelta(days=1))

  if 'coin2_style' in query_dict:
    coin2_style = query_dict['coin2_style']
    del query_dict['coin2_style']
  else:
    coin2_style = False

  need_filter_symbol = False
  if query_dict.get('subsymbols', None) is not None or query_dict.get('pick_symbol_regex', None) is not None:
    need_filter_symbol = True

  with XunkemgmtClient() as client:
    agg_types = [
      tss_pb2.STRATEGY_NAME,
      tss_pb2.STRATEGY_GROUP,
      tss_pb2.MACHINE,
      tss_pb2.MARKET_TYPE,
      tss_pb2.EXCHANGE,
      tss_pb2.OWNER,
      tss_pb2.TRADER,
    ]
    if need_filter_symbol:
      agg_types.append(tss_pb2.SYMBOL)
      agg_types.append(tss_pb2.TRADING_DATE)
    response = client.query_trading_summary_interval_histories(
      tss_pb2.QueryTradingSummaryIntervalHistoriesRequestProto(
        start_timestamp=start_ts,
        end_timestamp=end_ts,
        business_units=['coin', 'coinfof', 'prex', 'day1mm', 'extday1mm', 'digitalfund'],
        summary_info_only=True,
        agg_types=agg_types,
      )
    )
  data_list = []
  for proto in response.histories:
    summary_info = proto.summary_info
    data = {
      "machine": summary_info.machine,
      "market_type": summary_info.market_type,
      "exchange": summary_info.exchange,
      "owner": summary_info.owner,
      "strategy_group": summary_info.strategy_group,
      "strategy_name": summary_info.strategy_name,
      "trader": summary_info.trader,
    }
    if need_filter_symbol:
      data["symbol"] = summary_info.symbol
      data["trading_date"] = datetime.datetime.strptime(str(summary_info.trading_date), "%Y%m%d").date()
    data_list.append(data)

  df = pd.DataFrame(data_list)
  df.drop_duplicates(inplace=True)
  df.reset_index(drop=True, inplace=True)

  if not need_filter_symbol:
    return list(df.T.to_dict().values())
  if len(df) == 0:
    return []

  dfrows = []
  subsymbols = []
  for _, dfrow in df.iterrows():
    try:
      dtz = datetime.datetime.combine(dfrow.trading_date, datetime.datetime.min.time())
      product = generate_product_from_str2(market_type=dfrow.market_type,
                                           exchange=dfrow.exchange,
                                           api_version=None,
                                           norm_product=dfrow.symbol,
                                           current_datetime=dtz)
      subsymbol = product.subscription_symbol if hasattr(
          product, 'subscription_symbol') else product.symbol
      if coin2_style:
        for k, v in COIN2_RELATIVE_SYMBOL_MAP.items():
          subsymbol = subsymbol.replace(k, v)
      subsymbols.append(subsymbol)
      dfrows.append(dfrow)
    except Exception:
      # usually due to expiry
      continue
  assert df.shape[0] == len(subsymbols)
  df['subsymbol'] = subsymbols

  for qkey, qval in query_dict.items():
    if qkey == "subsymbols":
      if qval is None:
        continue
      sel = None
      ssymbols = qval.split(",")
      for ssymbol in ssymbols:
        exchange, market_type, symbol = ssymbol.split(':')
        product = generate_product_from_str2(market_type=market_type,
                                             exchange=exchange,
                                             api_version=None,
                                             norm_product=symbol,
                                             current_datetime=trading_date)
        asel = (df.exchange == product.exchange) & (df.market_type
                                                    == market_type) & (df.symbol == product.symbol)
        if sel is None:
          sel = asel
        else:
          sel |= asel
      df = df.loc[sel]
    elif qval is not None:
      if qkey == 'strategy_name':
        regex = re.compile(qval)
        chosen = []
        for rowqval in df.loc[:, qkey]:
          chosen.append(regex.match(rowqval) is not None)
        df = df.loc[chosen]
      elif qkey == "pick_symbol_regex":
        qkey = 'symbol'
        regex = re.compile(qval)
        chosen = []
        for rowqval in df.loc[:, qkey]:
          chosen.append(regex.match(rowqval) is not None)
        df = df.loc[chosen]
      else:
        df = df.loc[df.loc[:, qkey] == qval]
  df.drop_duplicates(inplace=True)
  stratlist = list(df.T.to_dict().values())
  return stratlist


def iterate_strat_config(root_path):
  for globpatt in [
      "coin_deploy/lm_agg2/*/*.json",
      "coin_deploy/lm_volume/**/*.json"]:
    globpatt = os.path.join(root_path, globpatt)
    for stratfile in glob.glob(globpatt, recursive=True):
      yield "lm_sim", stratfile
  for globpatt in ["coin_deploy/hk_mm/v1_dumb/*/*.sh"]:
    globpatt = os.path.join(root_path, globpatt)
    for stratfile in glob.glob(globpatt, recursive=True):
      yield "unhedged", stratfile
  for globpatt in ["coin_deploy/basis_strat2/driver/*.json"]:
    globpatt = os.path.join(root_path, globpatt)
    for stratfile in glob.glob(globpatt, recursive=True):
      yield "basis_smm2", stratfile


def check_valid_strategy_request(strategy_request):
  return strategy_request is not None and \
      'strategy_name' in strategy_request and \
      'strategy_group' in strategy_request


def get_strat_name_conf_map(root_path='python'):
  name_conf_map = {}
  for sim_type, stratfile in iterate_strat_config(root_path):
    try:
      if stratfile.endswith('sh'):
        parsed = open(stratfile).read().split('echo \'')[1].split('\')')[0]
        stratconf = ujson.loads(clean_json(parsed))
      else:
        stratconf = ujson.load(open(stratfile))
    except Exception:
      import traceback
      traceback.print_exc()
      print(f"failed to load {stratfile}")
      continue

    strategy_request = stratconf.get('strategy') or \
                       stratconf.get('common', {}).get('strategy_request')
    if not check_valid_strategy_request(strategy_request):
      continue

    name_conf_map[strategy_request['strategy_name']] = {
        'stratfile': stratfile,
        'stratconf': stratconf,
        'sim_type': sim_type,
        'strategy_name': strategy_request['strategy_name'],
        'strategy_group': strategy_request['strategy_group']
    }
  return name_conf_map


def iterate_strategy(root_path, trading_date, strategy_group, strategy_name, subsymbols):
  stratinfos = get_strategy_info(trading_date=trading_date,
                                 strategy_group=strategy_group,
                                 strategy_name=strategy_name,
                                 subsymbols=subsymbols)
  name_conf_map = get_strat_name_conf_map(root_path)
  for stratinfo in stratinfos:
    if stratinfo['strategy_name'] not in name_conf_map:
      print("# omitting %s" % stratinfo['strategy_name'])
      continue
    outdict = name_conf_map[stratinfo['strategy_name']]
    yield stratinfo, (outdict['stratfile'], outdict['stratconf'], outdict['sim_type'])


def get_strategy_infos(ori_date, ori_name, strategy_group='lm-agg2'):
  tds = abase.get_trading_dates(ori_date)
  lm_strategy_infos = get_strategy_info(
      trading_date=tds,
      strategy_group=strategy_group,
      strategy_name=ori_name)
  lm_name_conf_map = get_strat_name_conf_map()
  for lm_row in lm_strategy_infos:
    if lm_row['strategy_name'] in lm_name_conf_map:
      lm_row['driver'] = lm_name_conf_map[lm_row['strategy_name']]['stratfile']
  lmdf = pandas.DataFrame(lm_strategy_infos)
  lmdf['name'] = lmdf['strategy_name']
  lmdf['host'] = lmdf['machine']
  return lmdf
